#ifndef DEF_Vect
#define DEF_Vect

// --- Includes --------------------------------------------------------------

#include <cmath>

// --- Classes ---------------------------------------------------------------

// --- Vect2

template <typename T>
struct Vect2 
{
	Vect2() {}
	Vect2(T x, T y) : x(x), y(y) {}

	T Dot(const Vect2& v) const
	{
		return x * v.x + y * v.y;
	}

	Vect2 operator+(const Vect2& v) const
	{
		return Vect2(x + v.x, y + v.y);
	}

	Vect2 operator-(const Vect2& v) const
	{
		return Vect2(x - v.x, y - v.y);
	}

	void operator+=(const Vect2& v)
	{
		*this = Vect2(x + v.x, y + v.y);
	}

	void operator-=(const Vect2& v)
	{
		*this = Vect2(x - v.x, y - v.y);
	}

	Vect2 operator/(float s) const
	{
		return Vect2(x / s, y / s);
	}

	Vect2 operator*(float s) const
	{
		return Vect2(x * s, y * s);
	}

	void operator/=(float s)
	{
		*this = Vect2(x / s, y / s);
	}

	void operator*=(float s)
	{
		*this = Vect2(x * s, y * s);
	}

	void Normalize()
	{
		float s = 1.0f / Length();
		x *= s;
		y *= s;
	}

	Vect2 Normalized() const 
	{
		Vect2 v = *this;
		v.Normalize();
		return v;
	}

	T LengthSquared() const
	{
		return x * x + y * y;
	}

	T Length() const
	{
		return sqrt(LengthSquared());
	}

	operator Vect2<float>() const
	{
		return Vect2<float>(x, y);
	}

	bool operator==(const Vect2& v) const
	{
		return x == v.x && y == v.y;
	}

	T x, y;
};

// --- Vect3

template <typename T>
struct Vect3 
{
	inline Vect3() : x (0), y (0), z (0){} 
	inline Vect3(T x, T y, T z) : x(x), y(y), z(z) {}

	inline Vect3(const Vect3<T>& rhs)
	{
		x = rhs.x;
		y = rhs.y;
		z = rhs.z;
	}

	inline T Length()
	{
		return std::sqrt(x * x + y * y + z * z);
	}

	inline void Normalize()
	{
		float s = 1.0f / Length();
		x *= s;
		y *= s;
		z *= s;
	}

	inline Vect3 Normalized() const 
	{
		Vect3 v = *this;
		v.Normalize();
		return v;
	}

	inline float LengthSquared() const
	{
		return (x*x +y *y +z*z);
	}

	inline Vect3 NormalizedSafe() const 
	{
		Vect3 v = *this;
		if (v.LengthSquared() != 0.f)
			v.Normalize();
		else
			v = Vect3(0.f, 0.f, 0.f);
		return v;
	}

	inline Vect3 Cross(const Vect3& v) const
	{
		return Vect3(y * v.z - z * v.y,
			z * v.x - x * v.z,
			x * v.y - y * v.x);
	}

	inline T Dot(const Vect3& v) const
	{
		return x * v.x + y * v.y + z * v.z;
	}

	inline Vect3 operator+(const Vect3& v) const
	{
		return Vect3(x + v.x, y + v.y,  z + v.z);
	}

	inline Vect3 operator=(const Vect3& v) const
	{
		return Vect3(v.x, v.y, v.z);
	}

	inline void operator+=(const Vect3& v)
	{
		x += v.x;
		y += v.y;
		z += v.z;
	}

	inline void operator-=(const Vect3& v)
	{
		x -= v.x;
		y -= v.y;
		z -= v.z;
	}

	inline void operator/=(T s)
	{
		x /= s;
		y /= s;
		z /= s;
	}

	inline Vect3 operator-(const Vect3& v) const
	{
		return Vect3(x - v.x, y - v.y,  z - v.z);
	}

	inline Vect3 operator-() const
	{
		return Vect3(-x, -y, -z);
	}

	inline Vect3 operator*(T s) const
	{
		return Vect3(x * s, y * s, z * s);
	}

	inline Vect3 operator/(T s) const
	{
		return Vect3(x / s, y / s, z / s);
	}

	inline bool operator==(const Vect3& v) const
	{
		return x == v.x && y == v.y && z == v.z;
	}

	T x, y, z;
};


// --- Vect4

template <typename T>
struct Vect4 
{
	Vect4() {}
	Vect4(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) {}
	Vect4(const Vect3<T>& v, T w) : x(v.x), y(v.y), z(v.z), w(w) {}

	Vect4 operator+(const Vect4& v) const
	{
		return Vect4(x + v.x, y + v.y,  z + v.z, w + v.w);
	}

	T Dot(const Vect4& v) const
	{
		return x * v.x + y * v.y + z * v.z + w * v.w;
	}

	T x, y, z, w;
};


typedef Vect2<bool> bVector2;

typedef Vect2<int> iVector2;
typedef Vect3<int> iVector3;
typedef Vect4<int> iVector4;

typedef Vect2<float> Vector2;
typedef Vect3<float> Vector3;
typedef Vect4<float> Vector4;

#endif // DEF_Vect